﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Mvc;
using iTextSharp.text;
using iTextSharp.text.pdf;
using iTextSharp.tool.xml;
using iTextSharp.tool.xml.html;
using iTextSharp.tool.xml.parser;
using iTextSharp.tool.xml.pipeline.css;
using iTextSharp.tool.xml.pipeline.end;
using iTextSharp.tool.xml.pipeline.html;

namespace Mvc4Demo.Core
{
    public class PdfView : IView
    {
        private readonly string _viewPhysicalPath;

        public PdfView(string viewPhysicalPath)
        {
            _viewPhysicalPath = viewPhysicalPath;
        }

        public void Render(ViewContext viewContext, TextWriter writer)
        {
            //Load Template File
            string rawContents = File.ReadAllText(_viewPhysicalPath);

            //Perform Replacements with metadata
            string parsedContents = Parse(rawContents, viewContext.ViewData, viewContext.ViewData.Model);
            
            //conver to pdf data
            var memoryStream = PdfConverter.HtmlToPdf(parsedContents);

            WriteToResponse(viewContext.Controller.ControllerContext.HttpContext.Response, memoryStream, "Testpdf");

            //writer.Write(parsedContents);
            
        }

        protected string Parse(string contents, ViewDataDictionary viewdata, object model)
        {
            return Regex.Replace(contents, "\\{(.+)\\}", m => GetMatch(m, viewdata, model));
        }

        protected virtual string GetMatch(Match m, ViewDataDictionary viewdata, object model)
        {
            if (m.Success)
            {
                var metadata = m.Result("$1");
                var metadataValue = GetMetadataValue(metadata, model);
                if (metadataValue == null && viewdata.ContainsKey(metadata))
                {
                    return viewdata[metadata].ToString();
                }
            }
            return string.Empty;
        }

        //TODO : move this code into some common lib or class
        protected static ConcurrentDictionary<Type, IEnumerable<PropertyInfo>> PropertyInfos = new ConcurrentDictionary<Type, IEnumerable<PropertyInfo>>();

        protected object GetMetadataValue(string metadata, object model)
        {
            if (model == null)
                return null;

            var modelType = model.GetType();
            var modelPropertyInfos = PropertyInfos.GetOrAdd(modelType,
                                                            (t) => modelType.GetProperties());

            var propertyInfo = modelPropertyInfos.FirstOrDefault(x => x.Name == metadata);
            if (propertyInfo != null)
                return propertyInfo.GetValue(model, null);
            return null;
        }

        protected void WriteToResponse(HttpResponseBase response, MemoryStream memoryStream, string filename = null)
        {
            response.Clear();
            response.ClearContent();
            response.ContentType = "application/pdf";
            if (!string.IsNullOrEmpty(filename))
            {
                response.AddHeader("Content-Disposition", string.Format("attachment; filename={0}.pdf", filename));
            }
            response.BinaryWrite(memoryStream.ToArray());
            // myMemoryStream.WriteTo(Response.OutputStream); //works too

            memoryStream.Close();

            response.Flush();
            //response.Close();
            response.End();
        }
    }
}